from abc import ABC, abstractmethod
from uuid import uuid4

from aiogram import types, Router, F
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery
from aiogram.utils.keyboard import InlineKeyboardBuilder


def concatenate_key(*keyboards: types.InlineKeyboardMarkup | InlineKeyboardBuilder) -> InlineKeyboardBuilder:
    """
    Конкатенация инлайн клавиатур.

    :param keyboards: Инлайн клавиатуры.
    :return: Объединение инлайн клавиатур.
    """
    builder = InlineKeyboardBuilder()
    for key in keyboards:
        if isinstance(key, types.InlineKeyboardMarkup):
            builder.attach(InlineKeyboardBuilder.from_markup(key))
        elif isinstance(key, InlineKeyboardBuilder):
            builder.attach(key)
    return builder


class MyPaginator(ABC):
    def __init__(
            self,
            router: Router,
            current_page: int = 1,
            before_pagination_keyboards: list[types.InlineKeyboardMarkup] = None,
            after_pagination_keyboards: list[types.InlineKeyboardMarkup] = None,
            lines_on_page: int = 8):
        self.current_page = current_page
        self.pagination_hash = uuid4().hex
        self.router = router
        self.lines_on_page = lines_on_page
        self.before_pagination_keyboards = before_pagination_keyboards if before_pagination_keyboards else []
        self.after_pagination_keyboards = after_pagination_keyboards if after_pagination_keyboards else []
        self.max_page = 1

        self._build()
        if self.max_page > 1:
            self._register_handler()

    def _build(self):
        self.builder = concatenate_key(
            *self.before_pagination_keyboards,
            self._build_pagination_keyboard(),
            *self.after_pagination_keyboards
        )

    @abstractmethod
    def _build_pagination_keyboard(self) -> types.InlineKeyboardMarkup:
        """Создаем клавиатуру из своих данных.
        Так же нужно будет объявить self.max_page: int
        И в конце вызвать метод _add_pagination_buttons"""
        ...

    def _add_pagination_buttons(self,
                                builder: InlineKeyboardBuilder):
        current_page_index = types.InlineKeyboardButton(
                    text=f'{self.current_page}/{self.max_page}',
                    callback_data='None'
                )
        if self.current_page == 1 and self.max_page > 1:
            next_page = types.InlineKeyboardButton(
                text='⏩',
                callback_data=f'{self.pagination_hash}|{self.current_page + 1}'
            )
            builder.row(current_page_index, next_page)
        elif self.current_page > 1 and self.current_page != self.max_page:
            prev_page = types.InlineKeyboardButton(
                text='⏪',
                callback_data=f'{self.pagination_hash}|{self.current_page - 1}'
            )
            next_page = types.InlineKeyboardButton(
                text='⏩',
                callback_data=f'{self.pagination_hash}|{self.current_page + 1}'
            )
            builder.row(prev_page, current_page_index, next_page)
        elif self.current_page != 1 and self.current_page == self.max_page:
            prev_page = types.InlineKeyboardButton(
                text='⏪',
                callback_data=f'{self.pagination_hash}|{self.current_page - 1}'
            )
            builder.row(prev_page, current_page_index)

    def as_builder(self) -> InlineKeyboardBuilder:
        return self.builder

    def as_markup(self) -> types.InlineKeyboardMarkup:
        return self.builder.as_markup()

    async def _callback_handler(self,
                                call: CallbackQuery,
                                state: FSMContext) -> None:
        self.current_page = int(call.data.split("|")[-1])
        self._build()
        await call.message.edit_reply_markup(reply_markup=self.as_markup())
        await state.update_data({f'{self.__class__.__name__}_page': self.current_page})

    def _register_handler(self) -> None:
        self.router.callback_query.register(
            self._callback_handler,
            F.data.startswith(self.pagination_hash),
        )

    def get_start_stop_slice(self):
        start_slice = (self.current_page - 1) * self.lines_on_page
        end_slice = start_slice + self.lines_on_page

        return start_slice, end_slice

    def set_max_page_and_update_current_page(self, total_count: int) -> None:
        pages = total_count // self.lines_on_page
        pages += 1 if total_count % self.lines_on_page > 0 else 0
        self.max_page = pages
        if self.current_page > 1 and self.current_page > self.max_page:
            self.current_page -= 1
